#include    "ltsh.h"
#include    <sys/wait.h>
#include    <fcntl.h>
#include    <termios.h>

/* if user changed ps1, don't construct it*/
static int USER_PS1;

static void pr_env();
static void set_ps1();

static void lt_log_wait_status(int );
static void lt_log_func_begin(const char *);
static void lt_log_func_end(const char *);

static void sig_int(int);
static void sig_forking_int(int);
static void sig_forking_quit(int);
static void sig_cld_stop(int);

void pr_prompt()
{
	if (USER_PS1 == 0)
		set_ps1();
	printf("%s", getenv("PS1"));

	fflush(stdout);
}

void init_env()
{
	extern char	**environ;
	char		prompt[MAXLINE]; 
	char		login[MAXLINE];
	char		path[] = "/usr/sbin:/usr/bin:/sbin:/bin";
	
	environ = NULL;
	
	if (setenv("USER", getlogin(), 1) != 0)
		err_ret("putenv error");
	if (setenv("USERNAME", getlogin(), 1) != 0)
		err_ret("putenv error");
	
	sprintf(login, "/home/%s", getlogin());
	if (setenv("HOME", login, 1) != 0)
		err_ret("putenv error");
	if (chdir(login) < 0)
		err_ret("chdir error");
	
	if (setenv("PWD", login, 1) != 0)
		err_ret("putenv error");
	if (setenv("OLDPWD", login, 1) != 0)
		err_ret("putenv error");
	
	if (setenv("PATH", path, 1) != 0)
		err_ret("putenv error");
	/* for man ls */
	if (setenv("TERM", "xterm", 1) != 0)
		err_ret("putenv error");
	
	sprintf(prompt, "%s@ltsh:%s$ ", getlogin(), login);
	if (setenv("PS1", prompt, 1) != 0)
		err_ret("putenv error");

}

void init_shell()
{
	/* Ignore interactive and job-control signals.  */
	if (signal(SIGINT,  sig_int) == SIG_ERR ||
	    signal(SIGTSTP, SIG_IGN) == SIG_ERR ||
	    signal(SIGQUIT, SIG_IGN) == SIG_ERR)
		err_sys("init signal error");
}

int builtin(char *command)
{
	int ret = !strcmp(command, "export") ||
		  !strcmp(command, "exit") ||
		  !strcmp(command, "cd") ||
		  !strcmp(command, "fg");
	return ret;
}

void do_builtin(char *list[])
{
	if (!strcmp(list[0], "export"))
		do_export(list);
	else if (!strcmp(list[0], "exit"))
		do_exit(list);
	else if (!strcmp(list[0], "cd"))
		do_cd(list);
	else if (!strcmp(list[0], "fg"))
		do_fg(list);
}

void parse_line(char buf[], char *list[], unsigned int *flagp)
{
	lt_log_func_begin(__func__);

	char    *first, *end, *p = buf, *q;
	int     index = 0;
	int     len, i;
	int	in_quota = 0;

	*flagp = 0;

	while (*p != '\n') {
		while (*p == ' ') p++;

		/* handle '<', '>' and '>>' */
		if (*p == '<' || *p == '>') {
			if (*p == '<')
				set_bit(INPUT, flagp);
			else {
				p++;
				if (*p == '>')
					set_bit(APPEND, flagp);
				else {
					set_bit(OUTPUT, flagp);
					p--;
				}
			}
			p++;
			continue;
		}

		/* handle job control & */
		if (*p == '&') {
			set_bit(BACKGROUD, flagp);
			p++;
			continue;
		}

		/* handle left double quotation */
		if (*p == '\"') {
			p++;
			if (in_quota == 1) {
				in_quota = 0;
				continue;
			}
			in_quota = 1;
		}

		first = p;

		/* handle right double quotation */
		if (in_quota) {
			while (*p != '\"') p++;
		} else 
			while (*p != ' ' && *p != '\n' && 
			       *p != '<' && *p != '>' &&
			       *p != '&') p++;

		end = p;

		len = (int)(end - first);

		if (len != 0) {
			list[index] = (char *)malloc(len + 1);
			strncpy(list[index], first, len);
			list[index][len] = '\0';
			index++;
		}
	}

	for( i = 0 ; i < index ; i++ )
		lt_log("list[%d]=%s*\n", i, list[i]);

	lt_log_func_end(__func__);
}

void do_fork(char *list[], unsigned int flag, JobList *job_list[])
{
	lt_log_func_begin(__func__);

	int	status;
	pid_t	pid;
	char	**lastp;
	int	i;

	/* make output more better */
	if (signal(SIGINT, sig_forking_int) == SIG_ERR ||
	    signal(SIGQUIT, sig_forking_quit) == SIG_ERR)
		err_sys("signal wait error");

	if ((pid = fork()) < 0)
		err_sys("fork error");
	else if (pid == 0) {
		if (test_bit(BACKGROUD, &flag)) {
			if ((pid = fork()) < 0)
				err_sys("fork error");
			else if (pid > 0)
				exit(0);
		}
		int fd, newfd;
		unsigned int  openflag;

		/* TODO: we can't catch SIGTSTP:( */
		if (signal(SIGINT, SIG_DFL) == SIG_ERR ||
		    signal(SIGQUIT, SIG_DFL) == SIG_ERR || 
		    signal(SIGTSTP, sig_cld_stop) == SIG_ERR)
			err_sys("signal ignore error");

		if (test_bit(INPUT | OUTPUT | APPEND, &flag)) {
			/* TODO: next to '>' '<' '>>' is a filename*/
			lastp = get_filename(list);

			if (test_bit(INPUT, &flag)) {
				newfd = STDIN_FILENO;
				openflag = O_RDONLY | O_CREAT;
			} else if (test_bit(OUTPUT, &flag)) {
				newfd = STDOUT_FILENO;
				openflag = O_WRONLY | O_CREAT | O_TRUNC;
			} else if (test_bit(APPEND, &flag)) {
				newfd = STDOUT_FILENO;
				openflag = O_WRONLY | O_CREAT | O_APPEND;
			}
			if ((fd = open(*lastp, openflag, 0644)) < 0)
				err_sys("open error");
			if (dup2(fd, newfd) < 0)
				err_sys("dup2 error");
			free(*lastp);
			*lastp = NULL;
		}

		execvp(list[0], list);
		err_ret("couldn't execute: %s", list[0]);
		exit(127);
	}

	if ((pid = waitpid(pid, &status, WUNTRACED)) < 0)
		err_sys("waitpid error");
	lt_log_wait_status(status);

	/* recover the init signal */
	if (signal(SIGINT, sig_int) == SIG_ERR ||
	    signal(SIGQUIT, SIG_IGN) == SIG_ERR)
		err_sys("signal recover error");

	lt_log_func_end(__func__);
}

void do_export(char *list[])
{
	char *p, *newp, *value, *string;
	int len;

	if (list[1] == NULL)
		pr_env();
	else {
		p = list[1];

		while (*p != '=') p++;

		string = strndup(list[1], p-list[1]);
		value = ++p;
		lt_log("string:%s\n", string);
		lt_log("value:%s\n", value);

		if (setenv(string, value, 1) != 0)
			err_ret("setenv error");
		else if (!strcmp(string, "PWD")) {
			if (chdir(value) < 0)
				err_ret("chdir error");
		} else if (!strcmp(string, "PS1")) {
			USER_PS1 = 1;
		}
		free(string);
	}
}

void do_exit(char *list[])
{
	free_list(list);
	err_quit("logout");
}

void do_cd(char *list[])
{
	char *path, *curpath;

	if (list[1] == NULL)
		path = getenv("HOME");
	else if (!strcmp(list[1], "-"))
		path = getenv("OLDPWD");
	else
		path = list[1];

	if (chdir(path) < 0)
		err_ret("chdir error");

	curpath = get_current_dir_name();

	if (setenv("OLDPWD", getenv("PWD"), 1) != 0)
		err_ret("putenv error");
	if (setenv("PWD", curpath, 1) != 0)
		err_ret("putenv error");
	free(curpath);
}

void do_fg(char *list[])
{
    int job;

    if( list[1] == NULL )
        job = 1;
    else
        job = atoi(list[1]);
}
    

char **get_filename(char *list[])
{
	int i = 0;

	while( list[i] != NULL )
		i++;
	i--;

	return &list[i];
}

void free_list(char *list[])
{
	int i = 0;

	while (list[i] != NULL) {
		free(list[i]);
		list[i] = NULL;
		i++;
	}
}
static void pr_env()
{
	extern char **environ;
	int i;

	for (i = 0 ; environ[i] != NULL ; i++)
		printf("%d:%s\n", i, environ[i]);
}

static void lt_log_wait_status(int status)
{
	if (WIFEXITED(status))
		lt_log("normal termination, exit status = %d\n",
				WEXITSTATUS(status));
	else if (WIFSIGNALED(status))
		lt_log("abnormal termination, signal number = %d%s\n",
				WTERMSIG(status), "");
	else if (WIFSTOPPED(status))
		lt_log("child stopped, signal number = %d\n",
				WSTOPSIG(status));
}

static void lt_log_func_begin(const char *func)
{
	lt_log("\n-------BEGIN:%s-------\n", func);
}
static void lt_log_func_end(const char *func)
{
	lt_log("-------END:  %s-------\n\n", func);
}

static void sig_int(int signo)
{
	printf("\n");
	printf("%s", getenv("PS1"));
	fflush(stdout);
}
static void sig_forking_int(int signo)
{
	printf("\n");
	fflush(stdout);
}
static void sig_forking_quit(int signo)
{
	printf("Quit\n");
	fflush(stdout);
}
static void sig_cld_stop(int signo)
{
    /* never reach here,TODO */
    printf("\n");
    fflush(stdout);
    if( signal(SIGSTOP, SIG_DFL) == SIG_ERR )
    {
        err_sys("sigtstp error");
    }
    if( kill(SIGSTOP, getpid()) < 0)
    {
        err_sys("kill error");
    }
}

static void set_ps1()
{
	char prompt[MAXLINE]; 

	sprintf(prompt, "%s@ltsh:%s$ ", getlogin(), getenv("PWD"));

	if (setenv("PS1", prompt, 1) != 0)
		err_sys("putenv error");
}
